package com.iocup.keybastion.core.impl;


import com.iocup.keybastion.common.AuthConstant;
import com.iocup.keybastion.configuration.SecurityProperties;
import com.iocup.keybastion.context.SecurityContextHolder;
import com.iocup.keybastion.context.WebContextHolder;
import com.iocup.keybastion.core.HttpActionAdapter;
import com.iocup.keybastion.core.SecurityGrantedAccessAdapter;
import com.iocup.keybastion.core.SecurityLogicChecker;
import com.iocup.keybastion.core.client.Client;
import com.iocup.keybastion.core.client.ClientFinder;
import com.iocup.keybastion.core.credentials.Credentials;
import com.iocup.keybastion.core.profile.UserProfile;
import com.iocup.keybastion.exception.CredentialsEmptyException;
import com.iocup.keybastion.exception.CredentialsErrorException;
import com.iocup.keybastion.utils.PathUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;

import java.util.ArrayList;
import java.util.Optional;

/**
 * @author xyjxust
 * @create 2022/3/21 11:35
 **/
@Slf4j
public class DefaultLoginChecker implements SecurityLogicChecker {
    /**
     * 客户端查询器
     */
    private ClientFinder clientFinder;
    /**
     * 配置类
     */
    private SecurityProperties securityProperties;
    /**
     * 路径匹配工具
     */
    private PathUtil pathUtil = new PathUtil();



    public DefaultLoginChecker(ClientFinder clientFinder, SecurityProperties securityProperties) {
        this.clientFinder = clientFinder;
        this.securityProperties = securityProperties;
    }


    @Override
    public Object handle(WebContextHolder contextHolder, SecurityGrantedAccessAdapter accessAdapter, HttpActionAdapter actionAdapter) throws Exception {
        //获取请求路径
        String url = contextHolder.getRequestURL();
        //如果拦截路由中没有地址或者地址本身在放行路由中
        if (CollectionUtils.isEmpty(securityProperties.getExcludeList()) || pathUtil.isMatch(new ArrayList<>(securityProperties.getExcludeList()), url)) {
            return accessAdapter.adapt(contextHolder);
        }
        //不满足拦截路由
        if (!pathUtil.isMatch(new ArrayList<>(securityProperties.getIncludeList()), url)) {
            throw new SecurityException(String.format("该请求[%s]不在认证路由中", contextHolder.getRequestURL()));
        }
        //如果已经通过认证检查器了
        if (SecurityContextHolder.getContext().isAuth()) {
            return accessAdapter.adapt(contextHolder);
        }
        //如果没有登录,那么就进行获取token并加载用户配置信息
        Optional<Client> optionalClient = clientFinder.find(findClientName(contextHolder));
        if (!optionalClient.isPresent()) {
            throw new SecurityException("clientName错误");
        }
        //开始获取凭证信息
        Optional<Credentials> credentialsOptional = optionalClient.get().getCredentials(contextHolder);
        //凭证信息不存在就报错
        if (!credentialsOptional.isPresent()) {
            throw new CredentialsEmptyException("认证凭证为空");
        }
        Credentials credentials = credentialsOptional.get();
        //通过凭证信息获取该凭证的用户信息
        Optional<UserProfile> userProfile = optionalClient.get().getUserDetail(credentials, contextHolder);
        if (!userProfile.isPresent()) {
            throw new CredentialsErrorException("认证凭证错误");
        }
        //获取到用户信息后设置到上下文中，表示已经通过认证流程
        SecurityContextHolder.getContext().setUserProfile(userProfile.get());
        //继续下面的流程
        return accessAdapter.adapt(contextHolder);
    }


    private String findClientName(WebContextHolder contextHolder) {
        Optional<String> clientName = contextHolder.getRequestHeader(AuthConstant.LOGIN_CLIENT_NAME);
        if (!clientName.isPresent()) {
            clientName = contextHolder.getRequestParameter(AuthConstant.LOGIN_CLIENT_NAME);
        }
        if (!clientName.isPresent()) {
            clientName = Optional.of(this.securityProperties.getClient());
        }
        return clientName.get();
    }
}
